<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1925102018姜宇晨</title>
    <style>
        td {
            width: 100px;
            height: 30px;
            padding-left: 10px;
            border: 1px solid #000;
        }

    </style>
</head>
<body>
<form name="mainForm" >
    算法选择: RR<input type="radio" name="choose"value="1"> HRRN <input type="radio" name="choose"value="2"><br>
    时间片大小: <input type="number" name="quantity" id="rr">
</form>
<br>
请依次在每一行中输入进程名、到达时间、服务时间：
<br>
</body>

<script>
    //创建构造函数
    function Process(processName,arriveTime,serviceTime){
        this.processName = processName; //进程名
        this.arriveTime = Number(arriveTime); //到达时间
        this.servicetime = Number(serviceTime); //服务时间
        this.finishTime = -1; //完成时间 需计算的量初始值均设置为-1
        this.turnTime = -1; //周转时间
        this.turnTime_w = -1; //带权周转时间
        this.startTime = -1; //开始时间
        this.waitTime = -1; //等待时间
        this.leftTime = Number(serviceTime); //剩余服务时间(RR专用,初始值等于服务时间)
    }
    //获取进程数量
    var n = prompt("请输入进程数量：");
    var p = [];  //p数组存储结构体对象
    var chose = 0; //chose存储用户选择哪个算法
    var averTAT,averWTAT,averWT; //平均周转、平均带权周转、平均等待时间
    //calucate函数用于提取输入的数据，绑定到提交按钮，计算进程各项数据，在点击按钮时触发
    function calulate(){
        //获取文本框内容
        var c = document.getElementsByName('choose');
        var rr = Number(document.getElementById('rr').value);  //获取时间片
        //获取算法选择存储于chose
        for (var i=0; i<c.length; i++) {
            if (c[i].checked) {
                chose = c[i].value;
            }
        }
        //获取各个input框的输入值
        for (var i=0;i<n;i++){
            var pName = document.getElementById('p'+i+'0').value;
            var pArive = document.getElementById('p'+i+'1').value;
            var pServe = document.getElementById('p'+i+'2').value;
            var name = new Process(pName,pArive,pServe); //创建新对象
            p.push(name); //尾插进入p
        }
        //按到达时间早,服务时间短作为主要和次要关键字进行排序
        for (var i=0;i<n;i++){
            for (var j=i+1;j<n;j++){
                if(p[i].arriveTime>p[j].arriveTime||(p[i].arriveTime === p[j].arriveTime&&p[i].servicetime>p[j].servicetime)){
                    var temp = p[i];
                    p[i]=p[j];
                    p[j]=temp;
                }
            }
        }

        if(chose == 1){
            //若chose为1则代表选择了RR算法
            //RR计算各项数据
            var t = p[0].arriveTime,prev_t = 0;
            var queue = [],res = []; //RR队列和结果存储
            var readyQ2 = Array(100).fill('');  //按时间存储就绪队列数组
            queue.unshift(p.shift());
            //search用于寻找新加入进程
            function search(){
                for (var i = 0; i < p.length; i++) {
                    if(p[0].arriveTime >= prev_t && p[0].arriveTime <=t){
                        queue.push(p.shift());
                    }
                }
            }
            while (queue.length != 0){
                console.log(t);
                for (var i = 1; i < queue.length; i++) {
                    readyQ2[t] = readyQ2[t] + queue[i].processName;
                }
                if(queue[0].leftTime == queue[0].servicetime){
                    //若运行时服务时间等于剩余服务时间则代表首次运行，即可记录开始时间
                    queue[0].startTime = t;
                }
                if(queue[0].leftTime > rr){
                    prev_t = t; //记录上一个t,用于下面寻找当前时间片中新加入的进程
                    t = t + rr;
                    queue[0].leftTime = queue[0].leftTime - rr;
                    search();
                    queue.push(queue.shift()); //当前进程运行后push进尾部

                }else{
                    prev_t = t;
                    t = t + queue[0].leftTime;
                    queue[0].finishTime = t; //记录完成时间
                    res.push(queue.shift()); //运行完弹出该进程
                    search();
                }
            }
            p = res;
            for (var i = 0; i < p.length; i++) {
                p[i].turnTime = p[i].finishTime - p[i].arriveTime;
                p[i].turnTime_w = p[i].turnTime/p[i].servicetime;
                p[i].waitTime = p[i].turnTime - p[i].servicetime;
            }
            createRes(p,chose,readyQ2); //调用createRes函数
        }else if(chose == 2){
            //若chose为2则选择的是HRRN算法
            //计算数据
            for (var i=0;i<n;i++){
                //情况一：若是首个运行的进程 或 p[i]进程到达时上一个进程已结束，则该进程到达就开始运行
                if(i==0 || p[i].arriveTime>=p[i-1].finishTime){
                    p[i].startTime = p[i].arriveTime;
                }else{
                    //情况二：否则在上一个进程的运行时间段中搜索出已到达但未运行进程，在其中抓(冒泡)一个响应比最高的运行
                    for (var j = i;j<n-1;j++){
                        var respTime_1 = (p[i-1].finishTime - p[j].arriveTime + p[j].servicetime)/p[j].servicetime;
                        var respTime_2 = (p[i-1].finishTime - p[j+1].arriveTime + p[j+1].servicetime)/p[j+1].servicetime;
                        if(p[j].arriveTime<=p[i-1].finishTime && respTime_1 < respTime_2){
                            var temp = p[j];
                            p[j] = p[j+1];
                            p[j+1] = temp;
                        }
                    }
                    p[i].startTime = p[i-1].finishTime; //情况二下进程开始时间等于上一进程的结束时间
                }
                //计算各项数据
                p[i].finishTime = p[i].startTime + p[i].servicetime;
                p[i].turnTime = p[i].finishTime - p[i].arriveTime;
                p[i].turnTime_w = p[i].turnTime/p[i].servicetime;
                p[i].waitTime = p[i].turnTime - p[i].servicetime;
            }
            createRes(p,chose);
        }else{
            //若未选择算法则弹窗提示
            alert('请选择算法！');
        }
    }

    //动态生成表单，根据用户输入生成指定行的表单
    var o = document.body;
    function createInput(){
        for(var i = 0;i < n;i++){
            for(var j = 0;j<3;j++){
                var input = document.createElement('input');
                input.type = 'text';
                input.id = 'p'+i+j; //表单行索引设定为i,列索引设置为j，方便数据提取
                o.appendChild(input);
            }
            o.appendChild(document.createElement('br'));
        }
    }
    //动态生成按钮，使得按钮始终在表单后面
    function createBt(){
        var bt = document.createElement('input');
        bt.type = 'button';
        bt.name = 'bt';
        bt.value = '计算';
        bt.onclick = calulate; //按钮绑定calulate函数
        o.appendChild(bt);
    }
    //动态生成结果表格
    function createRes(p,chose,readyQ2){
        //计算各个平均时间
        var tt1=0,tt2=0,tt3=0;
        for (var i=0;i<n;i++){
            tt1 += p[i].turnTime;
            tt2 += p[i].turnTime_w;
            tt3 += p[i].waitTime;
        }
        averTAT = tt1/n;
        averWTAT = tt2/n;
        averWT = tt3/n;

        var readyQ = Array(100).fill(''); //readyQ存储就绪队列
        if(chose == 2){
            //计算HRRN就绪队列
            //此算法下进程运行顺序可变，已有队列会因为新加入的短进程插队而打乱顺序
            //因此需要对系统整个运行的时间段(到最后一个进程开始运行后截至)进行遍历，每个时间点搜索就绪进程存储在tempArr中
            //对tempArr按响应比排序即为该时刻的就绪队列
            for (var i = 1; i < p[n-1].finishTime; i++){
                var tempArr = [];
                for (var j=0;j<n;j++){
                    if(i>=p[j].arriveTime && i<p[j].startTime){
                        tempArr.push(p[j]);
                    }
                }
                //按响应比排序
                for (var j = 0;j<tempArr.length;j++){
                    for (var k = j+1;k<tempArr.length;k++){
                        var respTime_j = (i - tempArr[j].arriveTime + tempArr[j].servicetime)/tempArr[j].servicetime; //计算响应比
                        var respTime_k = (i - tempArr[k].arriveTime + tempArr[k].servicetime)/tempArr[k].servicetime;
                        if(respTime_k > respTime_j){
                            var temp = tempArr[j];
                            tempArr[j]=tempArr[k];
                            tempArr[k]=temp;
                        }
                    }
                    readyQ[i] = readyQ[i] + tempArr[j].processName;
                }
            }
        }else if(chose == 1){
            //计算RR的就绪队列
            readyQ = readyQ2;

        }
        //以下四个数组用于遍历数据生成表格输出
        var arr = ['进程名','到达时间','服务时间','开始执行时间','完成时间','周转时间','带权周转时间','等待时间'];
        var arr2 = ['processName','arriveTime','servicetime','startTime','finishTime','turnTime','turnTime_w','waitTime'];
        var arr3 = ['平均周转时间','带权平均周转时间','平均等待时间'];
        var arr4 = [averTAT,averWTAT,averWT];
        //输出作业表
        o.appendChild(document.createElement('br'));
        o.appendChild(document.createElement('br'));
        o.appendChild(document.createElement('br'));
        var tx = document.createElement('p');
        tx.innerText = chose == 1? 'RR算法结果如下：':'HRRN算法结果如下：';
        o.appendChild(tx);
        for (var i = 0;i<=n;i++){
            var tr = document.createElement('tr');
            for (var j = 0;j<8;j++){
                var td = document.createElement('td');
                if(i == 0){
                    td.innerText = arr[j];
                }else{
                    td.innerText = p[i-1][arr2[j]];
                }
                tr.appendChild(td);
            }
            o.appendChild(tr);
        }
        //输出平均数表格
        o.appendChild(document.createElement('br'));
        for (var i = 0;i<2;i++){
            var tr = document.createElement('tr');
            for (var j = 0;j<3;j++){
                var td = document.createElement('td');
                if(i == 0){
                    td.innerText = arr3[j];
                }else{
                    td.innerText = arr4[j];
                }
                tr.appendChild(td);
            }
            o.appendChild(tr);
        }
        //输出就绪队列
        o.appendChild(document.createElement('br'));
        var txt = document.createElement('p');
        txt.innerText = '每一时间的就绪队列如下：(左边为队列头部)';
        o.appendChild(txt);
        for (var i = 0;i<2;i++){
            var tr = document.createElement('tr');
            for (var j = 0;j<100;j++){
                if(readyQ[j] != ''){
                    var td = document.createElement('td');
                    if(i == 0){
                        td.innerText = j;
                    }else{
                        td.innerText = readyQ[j];
                    }
                    tr.appendChild(td);
                }
            }
            o.appendChild(tr);
        }
    }

    createInput(n); //生成表单
    createBt();    //按钮始终最后生成
</script>
</html>